#include "./Log/Log.hpp"

/*
    对日志系统进行压力测试：
        同步,异步日志器：
            多线程，单线程，每秒输出多少日志
            多线程，单线程，输出总日志耗时多少
            多线程，单线程，平均每秒输出多少日志量
*/

// 日志器名称，线程数量,单个日志消息的大小,日志信息的数量
void bench(const std::string& name, int thread_cnt,size_t msg_len,size_t msg_cnt)
{
    // 1.获取日志器
    Log::Logger::ptr lp = Log::GetLogger(name);
    
    // std::vector<std::thread> threads(thread_cnt);
    // 不能初始化!!!
    std::vector<std::thread> threads;
    // 2.单个线程输出量：
    size_t out_cnt = msg_cnt / thread_cnt;
    std::vector<size_t> per_thread_outmsg(thread_cnt,out_cnt);
    per_thread_outmsg[0] += msg_cnt % thread_cnt;

    // 3.每个线程处理日志的时间
    std::vector<double> cost_time(thread_cnt);
    // 4.构建日志消息
    std::string msg;
    msg.resize(msg_len,'a');

    std::cout << "线程数量：" << thread_cnt << std::endl;
    std::cout << "输出日志的数量：" << msg_cnt << "条" << std::endl;
    std::cout << "输出日志的总大小：" << msg_cnt * msg_len / 1024 << "KB" << std::endl;  
    // 5.日志输出
    for(int i = 0; i < thread_cnt; i++)
    {

        // emplace系列的接口：构造并插入对象
        // 线程是防拷贝的，可以使用移动构造
        // 我们使用匿名对象进行构造线程,也是可以的。线程支持移动构造！
        // emplace系列的接口()内部是,作为该类构造函数的参数!
        // threads.emplace_back(std::thread([&,i]()
        threads.emplace_back(std::thread([&,i]()
        {
            // c++11中提供的高精度的时间：high_resolution_clock, 单位是纳秒,返回time_point对象
            auto start = std::chrono::high_resolution_clock::now();
            for(int j = 0; j < per_thread_outmsg[i]; j++)
                    lp->fatal("%s",msg.c_str());
            auto end = std::chrono::high_resolution_clock::now();

            // duration_cast 将一个时间段的时间单位进行转换
            // duration<double> duration - 时间段,单位 - <type> 以double形式获取秒为单位的时间
            auto duration = std::chrono::duration_cast<std::chrono::duration<double>>(end - start);
            cost_time[i] = duration.count();

            // 单位时间内,一个线程输出多少日志量
            auto avg = per_thread_outmsg[i] / cost_time[i];
            std::cout << "线程-" << i <<" 耗时 " << cost_time[i] << "s" << std::endl;
            std::cout << "单位时间内,线程输出日志量 " << avg << "条" << std::endl;

        }));

    }
    // 6.等待线程退出
    for(auto& t : threads)
    {
        t.join();
    }
    // 7.计算总耗时 多线程中时间最长的那个即为总耗时
    double max_time = cost_time[0];
    for(auto& t : cost_time)
        max_time = std::max(t,max_time);
    std::cout << "总耗时：" << max_time << "s" << std::endl;
    std::cout << "每秒输出日志数量：" << msg_cnt / max_time << "条" << std::endl;
    std::cout << "每秒输出日志的大小：" << (msg_len * msg_cnt / 1024) / max_time << "KB"<< std::endl;
}

void sync_thread_test(int thread_cnt,const std::size_t msg_len,size_t msg_cnt)
{
    // 创建好日志器
    DF_DEBUG("同步日志器：");
    DF_DEBUG("*************************");
    Log::LoggerBuilder::ptr lb(new Log::GlobalLoggerBuilder);
    lb->BuildFormatter("%N::[%p][%f:%l] %m%n");
    lb->BuildLimitLevel(Log::LogType::DEBUG);
    lb->BuildLoggerName("g_async_logger");
    lb->BuildLoggerSinks<Log::RollBySizeLogSink>("./AsyncLog/RBysz-");
    lb->Build();
    bench("g_async_logger",thread_cnt,msg_len,msg_cnt);
    DF_DEBUG("*************************");

}
/*
    异步日志输出这里，启动非安全模式，纯内存写入，不去考虑实际落地时间。
    否则考虑实际落地时间，异步性能肯定比同步性能要低，因为异步要拷贝到内存中去，然后再落地到磁盘中。
*/
void async_thread_test(int thread_cnt,size_t msg_len,size_t msg_cnt)
{
    // 创建好日志器
    DF_DEBUG("异步日志器：");
    DF_DEBUG("*************************");
    Log::LoggerBuilder::ptr lb = std::make_shared<Log::GlobalLoggerBuilder>();
    lb->BuildFormatter("%N::[%p][%f:%l] %m%n");
    lb->BuildLimitLevel(Log::LogType::DEBUG);
    lb->BuildLoggerName("g_sync_logger");
    lb->BuildEnableAsyncUnSafe();
    lb->BuildLoggerSinks<Log::RollBySizeLogSink>("./SyncLog/RBysz-");
    lb->Build();
    bench("g_sync_logger",thread_cnt,msg_len,msg_cnt);
    DF_DEBUG("*************************");

}

int main()
{
    sync_thread_test(1,1,1000000);
    async_thread_test(1,1,1000000);
    sleep(1);
    sync_thread_test(3,1,1000000);
    async_thread_test(3,1,1000000);
    sleep(1);
    sync_thread_test(5,1,1000000);
    async_thread_test(5,1,1000000);
    return 0;
}